使用時機:多個class,有共同的屬性與規則
耦合( Coupling ),內聚( Cohesion )
耦合:相依的強度 → 每個class除了本身的屬性,還使用到別的class的屬性
內聚:相關的強度 → 每個class本身才有的屬性,其他class沒有
父類別( Super Class ),子類別( Sub Class ),繼承( extends )
一個類別使用extends
關鍵字來從另一個類別繼承,子類別就會繼承除了父類別的建構式之外的所有屬性和方法,其中一個子類別只能繼承一個父類別
class Dog extends Animal {//子類別Dog 去繼承父類別Animal
//...
}
建構式 Constructor
要用繼承,要先做好建構
執行順序:
initializer list (初始化引數列表)
class Point {
num x;
num y;
Point(this.x, this.y);
// 初始化列表在建構式scope 執行之前設定例項變數,x跟y變數,會經由建構式傳來的Map 資料去初始化
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
main() {
Point p = Point.fromJson({'x':4,'y':5}); //印出 In Point.fromJson(): (4, 5)
}
父類別的建構式
子類別的建構式
需要透過建構式滿足繼承的規則
當我們在新建子類別的物件時,如果其父類別的建構式有引數時,子類別需要通過建構式來滿足繼承的規則,子類別要繼承成功,一定要處理負責丟引數資料給父類別 → super(引數);,子類別透過在建構式執行super(引數); 把引數傳給父類別的建構,相當於是在 new 父類別(引數);
若父類別有沒有引數的建構式,子類別的建構式可不用加super()
只有在類別完全沒有設定任何建構式的時候,Dart 才會自動產生預設建構式( 沒有引數的建構式,其建構式裡也沒有定義要執行的動作 ),若之後有設定有引數的建構式了,但還是想要有一個沒引數的建構式,需要自己手動新增
父類別有無引數的建構式
class Person {
//Person無任何建構式,Dart 會自動預設,我們為了證明新增子類別物件時會去執行父類別的建構式,故在此新增一個無引數建構來印出我們預期的內容
Person() {
print('Person default constructor');
}
}
class Employee extends Person {
// Person 有預設建構式 (無引數建構)
// 子類別Employee 的建構式可不用加super()
Employee(Map data) {
print('Employee constructor $data');
}
}
main() {
Employee a = Employee({'x':4,'y':5});
/*印出 Person default constructor
Employee constructor {x: 4, y: 5}
*/
}
父類別沒有無引數的建構式
class Person {
Person(Map data) {
print('Person $data');
}
}
class Employee extends Person {
// Person 沒有無引數建構式
// 子類別Employee 的建構式需要滿足繼承的規則,一定要加super(data)
Employee(Map data) :super(data){
print('Employee $data');
}
}
main() {
Employee a = Employee({'x':4,'y':5});
/*印出 Person {x: 4, y: 5}
Employee {x: 4, y: 5}
*/
}
有多種建構時
繼承的子類別只須要選一個建構來滿足繼承規則
- 子類別的每個建構式只需super
一次
class Person {
String _firstName;
//主建構式為一個無引數建構式
Person() {
print('Person');
}
//命名建構式
Person.fromString(this._firstName) {
print('Person fromString $_firstName');
}
//命名建構式
Person.fromJson(Map data) {
print('Person fromJson $data');
}
}
class Employee extends Person {
// Person 有多種建構式,子類別Employee 的建構式只需選父類別的一個建構式來super,即能滿足繼承規則
num x;
num y;
Employee(Map data) {
//選無引數建構式,可不用加super()
print('Employee $data');
}
Employee.fromString(String name) : super.fromString(name) {
print('Employee fromString $name');
}
Employee.fromJson(Map data) : super.fromString(data['name']) {
print('Employee fromJson $data');
}
Employee.fromJson2(Map data)
: x = data['x'],
y = data['y'],
super.fromJson(data) {
print('Employee fromJson2 $data, ($x, $y)');
}
}
main() {
Employee a = Employee({'x': 1, 'y': 2});
Employee b = Employee.fromString('Ryder');
Employee c = Employee.fromJson({'name': 'Ryder'});
Employee d = Employee.fromJson2({'x': 5, 'y': 6});
/*印出
Person
Employee {x: 1, y: 2}
Person fromString Ryder
Employee fromString Ryder
Person fromString Ryder
Employee fromJson {name: Ryder}
Person fromJson {x: 5, y: 6}
Employee fromJson2 {x: 5, y: 6}, (5, 6)
*/
}
Field 與Function 的處理
Field:
父類別Field若不是 private (變數名前面有加_
),子類別可直接使用,若想使用或設定父類別private的屬性,需在父類別新增setter/getter的方法,通常除了常數都會加封裝private
Function:
Override → 置換掉的意思,非修改
當子類別有與父類別的方法,名稱一樣以及引數數量一樣,會強制要求遵守overrider規則,所以各引數型態也必須一樣,否則會編譯失敗
欲override的父類別方法,不能用private 封裝設定方法
super.方法應用 → 使用被override 掉的父類別方法
class Person {
void show(int data){
print("Person $data");
}
}
class Employee extends Person {
String show(int data){
super.show(data);
return "Employee $data";
}
}
main() {
Employee a = Employee();
var eShow = a.show(3); //印出 Person 3
print(eShow); //印出 Employee 3
}
又稱異質宣告物件
目的:與List搭配使用,為了使用List
去做分類的管理運用
語法:
父類別 物件 = new 子類別( [ 引數值 ] ) ;
new 在Dart 2後可以選擇不用加
會限制物件使用的方法數量,只有物件的宣告類別裡的方法才能使用
class Student {
String _name;
Student(this._name);
String getMessage() {
return "姓名 = $_name";
}
//尚未有show方法
}
class SocialGroupStudent extends Student {
int _society;
SocialGroupStudent(String name, this._society) : super(name);
String getMessage() {
return super.getMessage() + ", 社會 = $_society";
}
void show() {
print(getMessage());
}
}
class ScienceGroupStudent extends Student {
int _science;
ScienceGroupStudent(String name, this._science) : super(name);
String getMessage() {
return super.getMessage() + ", 自然 = $_science";
}
void show() {
print(getMessage());
}
}
main() {
ScienceGroupStudent a = new ScienceGroupStudent("a", 95);
SocialGroupStudent b = new SocialGroupStudent("b", 100);
a.show(); //印出 姓名 = a, 自然 = 95
b.show(); //印出 姓名 = b, 社會 = 100
//多型
Student c = new ScienceGroupStudent("c", 95);
/*
c.show();
會報錯:Error: The method 'show' isn't defined for the class 'Student'.,因為物件c的reference為Student,編譯時在Student找不到show()方法
所以想要使用 ScienceGroupStudent 的 show() 方法,需要在Student類別也新增一個show方法給 ScienceGroupStudent的show()來override
*/
}
故要修改為:
class Student {
String _name;
Student(this._name);
String getMessage() {
return "姓名 = $_name";
}
void show() {
/*
是為了多型產生的物件能使用此方法才加,執行時會先從子類別開始,故在此的內容並不重要,目的為多型時給子類別override,若有物件為實體化Student的物件,要使用到此方法,也可設計內容
*/
}
}
class SocialGroupStudent extends Student {
int _society;
SocialGroupStudent(String name, this._society) : super(name);
String getMessage() {
return super.getMessage() + ", 社會 = $_society";
}
void show() {
print(getMessage());
}
}
class ScienceGroupStudent extends Student {
int _science;
ScienceGroupStudent(String name, this._science) : super(name);
String getMessage() {
return super.getMessage() + ", 自然 = $_science";
}
void show() {
print(getMessage());
}
}
main() {
//多型
Student c = new ScienceGroupStudent("c", 95);
Student d = new SocialGroupStudent("d", 100);
c.show(); //印出 姓名 = c, 自然 = 95
d.show(); //印出 姓名 = d, 社會 = 100
}
物件轉型
class Student {}
class SocialGroupStudent extends Student {}
class ScienceGroupStudent extends Student {}
main() {
Student student = new ScienceGroupStudent();
print(student);
//印出Instance of 'ScienceGroupStudent',為ScienceGroupStudent的例項,但宣告的類別為Student
/*但是若直接
ScienceGroupStudent scienceGroupStudent = student;
會編譯失敗,當異質宣告時,宣告類別必須是實體化類別的父類別:
父類別 物件 = new 子類別( [ 引數值 ] ) ;
上面例子不符合規則
*/
//當有繼承關係時,可以運用轉型 ( casting )
ScienceGroupStudent scienceGroupStudent = student as ScienceGroupStudent;//降轉
print(scienceGroupStudent);
//印出Instance of 'ScienceGroupStudent',同student
//student宣告類別為Student,與SocialGroupStudent也有繼承關係,也可以轉型,編譯時也可以成功,但是執行時會出現 Unhandled Exception: type 'ScienceGroupStudent' is not a subtype of type 'SocialGroupStudent' in type cast
SocialGroupStudent socialGroupStudent = student as SocialGroupStudent;
print(socialGroupStudent);
//執行時相當於是在做
//SocialGroupStudent socialGroupStudent = new ScienceGroupStudent();
//故會報錯
}
今天提到了關於繼承的觀念,這些觀念都會在之後開發專案時會有大量的實作,而多型的觀念,我們將會在下一篇有很多關於多型的運用,包括抽象的觀念、介面等等